#!/bin/sh
#
# This script is used for building an encrypted home directory in a chroot jail
# (one home per jail). It will build the chroot jailed system using SuSE packages
# thorugh 'zypper'. The advantage of using packages to build the jail is that it
# becomes much easier to apply patches to the chroot jail in the future. The
# advantage of using one home per jail is that the user's jail can be configured
# with the programs that a certain user needs, not something that someone else needs.
# On the down side, these scripts only work on SuSE Linux; it is also possible to
# load less programs into a jail by playing around with 'ldd'.
#
# Once the jail is built Advanced Intrustion Detection Environment (AIDE)
# http://www.cs.tut.fi/~rammer/aide.html
# is run to initialize a database for that jail. This database can be used to
# determine if any of the files in the jail have been change.
#
# Before applying patches, AIDE is run on the jail to see if anything
# has changed since the last time patches were installed. After the patches are
# applied AIDE is again run on the jail to build the database for the next
# itteration of patch applications.
#
# The home directory for the user in the jail is created using 'encfs'.
# The home is mounted as the user is 'logging in' from a special shell that
# is used to setup the user's environment withing the jail.
#
# Other scripts are available for: deleting the user, applying patches to all jails
# on the system based on the packages each user is using, and adding addiitonal
# programs to the chroot jail for a user.
#
# One side effect of the change/add scripts is that a search is made of the jail
# for any programs with the setgid/setuid bit, files found will have this bit cleared.
#
# In addition, if an openSSH server is setup to use only 'shared secret'
# authentication, this setup can arguably amount to two factor authentication.
# The two factors are: a reasonably large password protected private key
# on the computer accessing the server (e.g., user's laptop, I use a size of 4K),
# and the password needed to unencrypt the user's home directory.
#
# It is prudent to enforce idle timeouts for a user in the jail. The timeout
# will logout the user (kill their shell) and remove the clear text associated
# with the encrypted text of their home directory.
#
# It is also prudent to encrypt the server file system and swap with a LVM
# encryption method. This is not strictly necessary since the user home
# directories are only available as clear text when the user is logged in.
#
# Finally, setting up the syslog on your server to forward logs to
# another place is a good idea since it makes the task of "covering up the
# traks" after a breakin more difficult.
#
# Note: a chroot dirctory with just the basic system (e.g, nothing from
# 'addprog_chroot') is about 95 MB.
#
# The security provided by these scripts and the programs that they use
# is not as secure as giving each user a virtual computer. Here,
# if a breakin occurs that involves gaining root privileges, the server
# is compromised. With the virtual computers, only the virtual computer
# instance is compromised. Given the current state of the art, the virtual
# computer solution is more difficult to maintain. However, even a root
# breakin is not sufficient to allow viewing of the encrypted user home
# directories.
# 
#
# enchroot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# enchroot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with enchroot. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (c) 2010 charles@kollar.com
#

#
# Global configuraiton/options are set in this file...
. /usr/local/etc/enchroot.conf

##
## You should not have to change anything below this!
##

echo -n "Am I root?  "
if [ "$(whoami &2>/dev/null)" != "root" ] && [ "$(id -un &2>/dev/null)" != "root" ] ; then
  echo "  NO!

Error: You must be root to run this script."
  exit 1
fi
echo "  Yes";

# Check existence of necessary files
echo -n "Checking for which... " 
#if [ -f $(which which) ] ;
# not good because if which does not exist I look for an 
# empty filename and get OK nevertheless
if [ -f /usr/bin/which -o -f /bin/which -o -f /sbin/which -o -f /usr/sbin/which ] ; then
  echo "  OK";
else
  echo "  failed

Please install which binary!
"
exit 1
fi

echo -n "Checking for chroot..." 
if [ `which chroot` ] ; then
  echo "  OK";
else
  echo "  failed

chroot not found!
Please install chroot package/binary!
"
exit 1
fi

echo -n "Checking for sudo..." 
if [ `which sudo` ]; then
  echo "  OK";
else 
  echo "  failed

sudo not found!
Please install sudo package/binary!
"
exit 1
fi

echo -n "Checking for dirname..." 
if [ `which dirname` ]; then
  echo "  OK";
else 
  echo "  failed

dirname not found!
Please install dirname binary (to be found eg in the package coreutils)!
"
exit 1
fi

echo -n "Checking for awk..." 
if [ `which awk` ]; then
  echo "  OK";
else 
  echo "  failed

awk not found!
Please install (g)awk package/binary!
"
exit 1
fi

echo -n "Checking for encfs..." 
if [ `which encfs` ]; then
  echo "  OK";
else 
  echo "  failed"
  zypper install --no-recommends --auto-agree-with-licenses --type package encfs
  echo "Please rerun this script!"
  exit 1
fi

echo -n "Checking for aide..." 
if [ `which aide` ]; then
  echo "  OK";
else 
  echo "  failed"
  zypper install --no-recommends --auto-agree-with-licenses --type package aide
  echo "Please rerun this script!"
  exit 1
fi


SECFILE=/etc/sysconfig/security

sysperms=`grep '^PERMISSION_SECURITY=' $SECFILE | awk -F = '{print $2}'`

if ! [ "$sysperms" = "\"secure\"" ] ; then
  echo "Please change the entry for PERMISSION_SECURITY in '$SECFILE' to PERMISSION_SECURITY=\"secure\","
  echo "then run 'chkstat -set /etc/permissions'."
  exit 1
fi

#
# If this is the first time that this script has been run, we need to create some
# files first...

FIRSTTIME="no"
if ! [ -d "$HOME_ENCRYPTED" ] ; then
  echo -n "Expecting to find files that are not there. Is this the first time that this script has run on a system? Create these files?"
  read -p "[yes/no] (no): " FIRSTTIME
fi
if [ "$FIRSTTIME" == "yes" ]; then
  if ! [ -d "$HOME_CHROOT" ] ; then
    echo "Creating $HOME_CHROOT..."
    mkdir -p $HOME_CHROOT
    chown root:root $HOME_CHROOT
    chmod 755 $HOME_CHROOT
  fi
  if ! [ -d "$HOME_ENCRYPTED" ] ; then
    echo "Creating $HOME_ENCRYPTED..."
    mkdir -p $HOME_ENCRYPTED
    chown root:root $HOME_ENCRYPTED
    chmod 700 $HOME_ENCRYPTED
  fi
# This directory is used by openSSH to keep the user's public key that are used for
# 'shared secret'authentication. The 'default' place to keep these keys is in the user's
# home directory. But since this is the mount point for the user's encrypted file system
# we can't put them there.
  if ! [ -d "$PUBLIC_KEYS" ] ; then
    echo "Creating $PUBLIC_KEYS..."
    mkdir -p $PUBLIC_KEYS
    chown root:root $PUBLIC_KEYS
    chmod 755 $PUBLIC_KEYS
  fi
fi

CHROOT_USERNAME=$1

# Check if user already exists and ask for confirmation
# we have to trust that root knows what she is doing when saying 'yes'
if ( id $CHROOT_USERNAME > /dev/null 2>&1 ) ; then {
  echo "User $CHROOT_USERNAME already exists?!"
  echo "Please delete the user, or choose another name."
  exit 1
}
fi

JAILPATH=$HOME_CHROOT/$CHROOT_USERNAME
HOMEDIR="$JAILPATH/home/$CHROOT_USERNAME"

#
# This is necessary to give the user the specific privileges that they need to go into
# the jail that has been created for them. The commands that use these are found in the
# user's shell (chroot_shell).
echo "Modifying /etc/sudoers..."
echo "$CHROOT_USERNAME ALL=NOPASSWD: `which mount` -o bind /dev $JAILPATH/dev" >> /etc/sudoers
echo "$CHROOT_USERNAME ALL=NOPASSWD: `which chroot`, `which encfs`, `which su` - $CHROOT_USERNAME" >> /etc/sudoers

echo "Adding User and Group \"$CHROOT_USERNAME\" to system"
groupadd $CHROOT_USERNAME
# This will create the user's home directory...
useradd -m -d $HOMEDIR -g $CHROOT_USERNAME -s $CHROOT_SHELL $CHROOT_USERNAME && chmod 700 $HOMEDIR

# Here, we disable the password for the user since they will be using ssh with shared secrets to get a shell
# Note: If you don't do this you will get a complaint from sshd saying...
# 'User username not allowed because account is locked'
# Also, once the user is authenticated by shared secret they will be deposited into a chroot jail
# in which they have no data files (home directory). The $CHROOT_SHELL (see enchroot.conf) will
# prompt the user for a password to decrypt their data area.
echo -n "Disabling user's password... "
passwd -d $CHROOT_USERNAME

# creating ssh authentication files that need to be updated with half of the shared secret.
# If you don't get the ownership right you will get a "server refuses our key" message
# from the login window on putty. It should be setup so that the $PUBLIC_KEYS directory
# can be read by all, but every user's directories and files can only be read by them.
# Use the following entries in the 'sshd_config' file:
#RSAAuthentication yes
#PubkeyAuthentication yes
#AuthorizedKeysFile $PUBLIC_KEYS/%u/authorized_keys
mkdir -p $PUBLIC_KEYS/$CHROOT_USERNAME
chown ${CHROOT_USERNAME}:${CHROOT_USERNAME} $PUBLIC_KEYS/$CHROOT_USERNAME
chmod 700 $PUBLIC_KEYS/$CHROOT_USERNAME
touch $PUBLIC_KEYS/$CHROOT_USERNAME/authorized_keys
chown ${CHROOT_USERNAME}:${CHROOT_USERNAME} $PUBLIC_KEYS/$CHROOT_USERNAME/authorized_keys
chmod 600 $PUBLIC_KEYS/$CHROOT_USERNAME/authorized_keys

# The encrypted area for the user will have been made by 'encfs' above.
# It is also mounted by 'encfs' when the user logs in using $CHROOT_SHELL (see enchroot.conf).
if ! [ -d "$HOME_ENCRYPTED/$CHROOT_USERNAME" ] ; then
  mkdir -p $HOME_ENCRYPTED/$CHROOT_USERNAME
fi
chown ${CHROOT_USERNAME}:${CHROOT_USERNAME} $HOME_ENCRYPTED/$CHROOT_USERNAME
chmod 700 $HOME_ENCRYPTED/$CHROOT_USERNAME

touch $HOMEDIR/.Xauthority
chown ${CHROOT_USERNAME}:${CHROOT_USERNAME} $HOMEDIR/.Xauthority

# Since the user was created with 'useradd' we need to move the unencrypted
# data. So that there is no confusion, we delete the data from the unencrypted
# area so that it is now an empty mount point. This is a clue to the user that
# their password did not unencrypt their home directory.
#
# remove the data in the old directory...
rm -r $HOMEDIR/*
rm -r $HOMEDIR/.*

# need to be able to use this as $HOME before encfs is run at login...
chown ${CHROOT_USERNAME}:${CHROOT_USERNAME} $HOMEDIR

# At this point we need to mount the encrypted file system onto the user's
# home directory, and install files in their home directory...
# IMPORTANT NOTE:
# If you 'su' to the user created here while the encrypted device is mounted
# from this script, all of the files that are created will be owned by root
# even though 'id' says that the user id is that of the user created here.
echo "Mounting encrypted user home directory..."
/usr/bin/encfs -o allow_other -o nonempty -i 2 $HOME_ENCRYPTED/$CHROOT_USERNAME $HOMEDIR
echo "Install the home directory on the encrypted file system (now mounted)..."
(cd /etc/skel; tar cf - .) | (cd $HOMEDIR; tar xf -)
chown -R ${CHROOT_USERNAME}:${CHROOT_USERNAME} $HOMEDIR
chmod 700 $HOMEDIR

touch $HOMEDIR/.Xauthority
chown ${CHROOT_USERNAME}:${CHROOT_USERNAME} $HOMEDIR/.Xauthority

# delete some files that will not be needed, and make the protection stronger on others.
# NOTE: We should probably customize /etc/skel for our purposes in advance, or create
# a second version, say /usr/local/etc/skel that contains what we want.
rm -rf $HOMEDIR/.mozilla $HOMEDIR/public_html
rm -f $HOMEDIR/.emacs $HOMEDIR/.gnu-emacs $HOMEDIR/.vimrc $HOMEDIR/.xinitrc.template
# .inputrc handles keyboard mappings.
chmod -f 600 $HOMEDIR/.bashrc $HOMEDIR/.inputrc $HOMEDIR/.profile
chmod -f 700 $HOMEDIR/.fonts $HOMEDIR/.fontconfig $HOMEDIR/bin

#
# Make some of the usual directories in the jail and copy necessary files...

mkdir -p ${JAILPATH}/etc
# So that the resolver works...
cp /etc/resolv.conf ${JAILPATH}/etc

mkdir -p ${JAILPATH}/usr/local/etc
cp /usr/local/etc/enchroot.conf ${JAILPATH}/usr/local/etc

mkdir -p ${JAILPATH}/usr/local/bin
cp $CHROOT_SHELL ${JAILPATH}/usr/local/bin

#
# The directories where we will place the user's aide files....
USERAIDEDIR=$AIDEDIR/$CHROOT_USERNAME

mkdir -p $USERAIDEDIR
chmod 700 $AIDEDIR
chmod 700 $USERAIDEDIR
# use vertical bars as the RE seperator because the paths have '/' characters in them...
sed "s|%%(JAILPATH)|$JAILPATH|g" /usr/local/etc/aide.conf | sed "s|%%(USERAIDEDIR)|$USERAIDEDIR|g" > $USERAIDEDIR/aide.conf
chmod 400 $USERAIDEDIR/aide.conf

if ! [ -f ${JAILPATH}/etc/hosts ] ; then
  # note: even with this it doesn't work, resolve must no be happening so in the client we need to do
  # export DISPLAY=127.0.0.1:10.0 for x11 forwarding to work in the jail
  touch ${JAILPATH}/etc/hosts
  chown root:root ${JAILPATH}/etc/hosts
  chmod 644 ${JAILPATH}/etc/hosts
fi

echo "modifying /etc/hosts so that localhost works..."
echo "127.0.0.1      localhost" >> ${JAILPATH}/etc/hosts

if ! [ -f ${JAILPATH}/etc/group ] ; then
  echo "Creating the /etc/group file..."
  touch ${JAILPATH}/etc/group
  chown root:root ${JAILPATH}/etc/group
  chmod 644 ${JAILPATH}/etc/group
fi
echo "Adding the user's group to the group file..."
grep /etc/group -e "^${CHROOT_USERNAME}" > ${JAILPATH}/etc/group
# this is for /etc/shadow file...
grep /etc/group -e "^shadow" >> ${JAILPATH}/etc/group
# add the group for all users to etc/group (otherwise there is a nasty error
# message and probably because of that changing directories doesn't work with winSCP)
grep /etc/group -e "^users" >> ${JAILPATH}/etc/group
# these are groups that are need by the openSUSE package...
grep /etc/group -e "^lp" >> ${JAILPATH}/etc/group
grep /etc/group -e "^news" >> ${JAILPATH}/etc/group
grep /etc/group -e "^dialout" >> ${JAILPATH}/etc/group
grep /etc/group -e "^uucp" >> ${JAILPATH}/etc/group
grep /etc/group -e "^man" >> ${JAILPATH}/etc/group

# Add users to etc/passwd
# check if file exists (ie we are not called for the first time)
# if yes skip root's entry and do not overwrite the file

if ! [ -f ${JAILPATH}/etc/passwd ] ; then
  echo "Creating the /etc/password file..."
  touch ${JAILPATH}/etc/passwd
  chown root:root ${JAILPATH}/etc/passwd
  chmod 644 ${JAILPATH}/etc/passwd
fi
echo "Adding accounts to the passwd file..."
grep /etc/passwd -e "^root" >> ${JAILPATH}/etc/passwd
# these are users that are need by the openSUSE package...
grep /etc/passwd -e "^news" >> ${JAILPATH}/etc/passwd
grep /etc/passwd -e "^uucp" >> ${JAILPATH}/etc/passwd
grep /etc/passwd -e "^man" >> ${JAILPATH}/etc/passwd
grep /etc/passwd -e "^nobody" >> ${JAILPATH}/etc/passwd
grep /etc/passwd -e "^wwwrun" >> ${JAILPATH}/etc/passwd
grep /etc/passwd -e "^mail" >> ${JAILPATH}/etc/passwd
grep /etc/passwd -e "^lp" >> ${JAILPATH}/etc/passwd

# grep the username which was given to us from /etc/passwd and add it
# to ./etc/passwd replacing the $HOME with the directory as it will then 
# appear in the jail
echo "Adding User $CHROOT_USERNAME to jail..."
grep -e "^$CHROOT_USERNAME:" /etc/passwd | \
 sed -e "s#$JAILPATH##"      \
     -e "s#$CHROOT_SHELL#/bin/bash#"  >> ${JAILPATH}/etc/passwd

if ! [ -f ${JAILPATH}/etc/shadow ] ; then
  echo "Creating the /etc/shadow file..."
  touch ${JAILPATH}/etc/shadow
  chown root:shadow ${JAILPATH}/etc/shadow
  chmod 640 ${JAILPATH}/etc/shadow
fi
echo "Writing the user's line from /etc/shadow to their copy..."
grep -e "^$CHROOT_USERNAME:" /etc/shadow >> ${JAILPATH}/etc/shadow

#
# Changes to the /etc/profile can be lost during system upgrades.
# It is advisable to use the /etc/profile.local file for local settings
PROFILE_LOCAL=${JAILPATH}/etc/profile.local

if ! [ -f $PROFILE_LOCAL ] ; then
  echo "Creating the '$PROFILE_LOCAL' file..."
  touch $PROFILE_LOCAL
  chown root:root $PROFILE_LOCAL
  # NOTE: the user must be able to read this file...
  chmod 644 $PROFILE_LOCAL
fi

# set the mask so that no one other than the user has access by default.
echo "Modifying $PROFILE_LOCAL: setting mask; idle timeout; set DISPLAY..."
echo "umask 077" >> $PROFILE_LOCAL

# set the shell to logout after one hour (60*60=3600 sec) of inactivity...
IDLE_TIMEOUT_SEC=`dc -e "60 ${IDLE_TIMEOUT} *p"`
echo "export TMOUT=$IDLE_TIMEOUT_SEC" >> $PROFILE_LOCAL
echo "readonly TMOUT" >> $PROFILE_LOCAL

#
# this will set the display for the user to the correct thing if the user
# has x11 capabilities...
echo 'if [ "$DISPLAY" == "" -a -x /usr/bin/xauth ] ; then' >> $PROFILE_LOCAL
echo "  DISPLAY_NUM=\`xauth -bi list | tail -1 | awk -F ' ' '{print \$1}' | awk -F : '{print \$2}'\`" >> $PROFILE_LOCAL
echo '  export DISPLAY="localhost:$DISPLAY_NUM"' >> $PROFILE_LOCAL
echo '  echo DISPLAY=$DISPLAY' >> $PROFILE_LOCAL
echo 'fi' >> $PROFILE_LOCAL

# While /dev is used as a mount point for '/dev' on the server we will experiment
# with creating some files in '/dev' here. It would be more secure to do things
# this way. Sadly, zypper does not provide a way of telling which files in '/dev'
# need to be created for the package being added, nor does it add these.
#
# Create some files in /dev...
# http://www.linuxfromscratch.org/pipermail/lfs-support/2007-December/033877.html
mkdir -p ${JAILPATH}/dev
/bin/mknod ${JAILPATH}/dev/null c 1 3
/bin/mknod ${JAILPATH}/dev/zero c 1 5

#
# Define some repositories for the chroot jailed user. The repositories are for:
# open source programs, non-open source programs, and updates.
OSS=http://download.opensuse.org/distribution/$SUSE_VERSION/repo/oss/
NONOSS=http://download.opensuse.org/distribution/$SUSE_VERSION/repo/non-oss/
UPDATE=http://download.opensuse.org/update/$SUSE_VERSION/

#
# http://en.opensuse.org/Upgrade/Supported
# Add the repositories to the chroot jail...
echo
echo "Installing repositories in the chroot jail..."
echo
zypper --root $JAILPATH addrepo --refresh --name "openSUSE-$SUSE_VERSION OSS" $OSS repo-oss
zypper --root $JAILPATH addrepo --refresh --name "openSUSE-$SUSE_VERSION Non-OSS" $NONOSS repo-non-oss
zypper --root $JAILPATH addrepo --refresh --name "openSUSE-$SUSE_VERSION Updates" $UPDATE repo-update

# enable auto update of repository...
#zypper --root $JAILPATH modifyrepo -er repo-update

echo
echo "Listing repositories in the chroot jail..."
zypper --root $JAILPATH repos --uri

echo
echo "Installing base system..."
echo
zypper --root $JAILPATH install --no-recommends --auto-agree-with-licenses --type product openSUSE
# A change in 'encfs' from 1.5 to 1.6 requires it to be in the jail as well...
zypper --root $JAILPATH install --no-recommends --auto-agree-with-licenses --type package man-pages pam-modules awk diff unzip grep gzip less man sed tar vim sudo encfs iputils

# While this is a 64-bit install, some programs that get loaded are compiled for the 32-bit shared librarires...
zypper --root $JAILPATH install --no-recommends --auto-agree-with-licenses --type package glibc glibc-32bit libstdc++33-32bit

zypper --root $JAILPATH install --no-recommends --auto-agree-with-licenses --type package iputils net-tools

#zypper --root $JAILPATH search -dC "thing"
#zypper --root $JAILPATH list-patches
#zypper --root $JAILPATH verify


#
# Change the system (jail) wide permission to "secure"...

USERSECFILE=${JAILPATH}${SECFILE}

sysperms=`grep '^PERMISSION_SECURITY.*=.*' $USERSECFILE | awk -F = '{print $2}'`

if ! [ "$sysperms" = "\"secure\"" ] ; then
  echo "Changing the entry for PERMISSION_SECURITY in '$USERSECFILE' to PERMISSION_SECURITY=\"secure\"."
  SECFILETMP=/tmp/security.$$
  sed "s/^PERMISSION_SECURITY.*=.*/PERMISSION_SECURITY=\"secure\"/" $USERSECFILE > $SECFILETMP
  cp $SECFILETMP $USERSECFILE
  rm -f $SECFILETMP
  chroot $JAILPATH /sbin/SuSEconfig -module permissions
fi

sysperms=`grep '^PERMISSION_SECURITY.*=.*' $USERSECFILE | awk -F = '{print $2}'`

if ! [ "$sysperms" = "\"secure\"" ] ; then
  echo "Was not able to change PERMISSION_SECURITY entry in '$USERSECFILE' to PERMISSION_SECURITY=\"secure\"."
  exit 1
fi


#echo ""
#echo "Removing the suid/sgid bit for the following files..."
#find ${JAILPATH} \( -perm -004000 -o -perm -002000 \) -type f -exec ls -l {} \; -exec /bin/chmod -s {} \;
echo "The suid/sgid bit is set for the following files..."
find ${JAILPATH} \( -perm -004000 -o -perm -002000 \) -type f -exec ls -l {} \;

echo ""
echo "Creating user's aide database..."
aide --init --config=$USERAIDEDIR/aide.conf

HOME_P=`/bin/grep "encfs $JAILPATH/home/$USER" /etc/mtab`
if [ "$HOME_P" != "" ] ; then
  echo ""
  echo "Dismounting the user's encrypted home directory '$HOMEDIR'..."
  fusermount -u $HOMEDIR
fi

echo ""
echo "!! Remember to copy users public key to $PUBLIC_KEYS/$CHROOT_USERNAME/authorized_keys"

addprog_chroot $CHROOT_USERNAME
